package hud;

import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import event.EventBase;
import event.EventEnum;
import event.EventSystem;
import hud.data.CollectionInformation;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import org.bson.Document;
import org.fxmisc.richtext.StyleClassedTextArea;
import org.fxmisc.richtext.TextExt;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.*;

public class CollectionTab {
    private TableView tableView;
    private MongoCollection<Document> collection;
    private TreeTableView treeTableView;
    private ListView<Text> listView;
    private Control currentContentView;
    private VBox container;
    private WebView browser;

    private List<Control> viewList = new ArrayList<>();

    public Tab init(TabPane tabPane, CollectionInformation ci) {
        this.collection = ci.getMongoCollection();
        String tabName = collection.getNamespace().toString();
        Tab tab1 = new Tab(tabName);

        // Context menu
        MenuItem menuItem = new MenuItem("Close all");
        menuItem.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                EventSystem.instance().dispatch(new EventBase(EventEnum.closeAllTab, null));
            }
        });
        ContextMenu contextMenu = new ContextMenu(menuItem);

        tab1.setContextMenu(contextMenu);

        container = new VBox();
        container.prefHeightProperty().bind(tabPane.heightProperty());

        // 1、最上面的标签
        HBox hBox = new HBox();
        Label label = new Label();
        label.setPadding(new Insets(5, 0, 5, 10));
        label.textProperty().set(ci.getClientName());
        hBox.getChildren().add(label);
        label = new Label();
        label.setPadding(new Insets(5, 0, 5, 10));
        label.textProperty().set(ci.getHostName() + ":" + ci.getPort());
        hBox.getChildren().add(label);
        container.getChildren().add(hBox);

        // 2、标签下方的编辑框
        hBox = new HBox();
        hBox.prefWidthProperty().bind(tabPane.widthProperty());
        TextArea textArea = new TextArea();
        textArea.setPadding(new Insets(5, 5, 5, 5));
        textArea.setPrefRowCount(1);
        textArea.prefWidthProperty().bind(hBox.widthProperty());
        hBox.getChildren().add(textArea);
        container.getChildren().add(hBox);

        // 3、工具栏
        hBox = new HBox();
        hBox.setSpacing(5);
        hBox.setAlignment(Pos.CENTER_LEFT);
        Label tipLabel = new Label();
        tipLabel.setPadding(new Insets(5, 0, 5, 10));
        tipLabel.textProperty().set("0.0106 seconds");
        final Pane spacer = new Pane();
        HBox.setHgrow(spacer, Priority.ALWAYS);
        spacer.setMinSize(100, 0);
        Button treeButton = new Button("tree");
        treeButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                if (currentContentView != null) {
                    currentContentView.setVisible(false);
                    container.getChildren().remove(currentContentView);
                }
                if (treeTableView != null) {
                    treeTableView.setVisible(true);
                }
                else {
                    treeTableView = loadTableTreeView();
                }
                container.getChildren().add(treeTableView);
                treeTableView.prefHeightProperty().bind(container.heightProperty());
                currentContentView = treeTableView;
            }
        });
        Button tableButton = new Button("table");
        tableButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                if (currentContentView != null) {
                    currentContentView.setVisible(false);
                    container.getChildren().remove(currentContentView);
                }
                if (tableView != null) {
                    tableView.setVisible(true);
                }
                else {
                    loadFirstView();
                }
                container.getChildren().add(tableView);
                tableView.prefHeightProperty().bind(container.heightProperty());
                currentContentView = tableView;
            }
        });
        Button docButton = new Button("Document");
        docButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                if (currentContentView != null) {
                    currentContentView.setVisible(false);
                    container.getChildren().remove(currentContentView);
                }
                if (listView != null) {
                    listView.setVisible(true);
                }
                else {
                    listView = loadTextView();
                }
                container.getChildren().add(listView);
                listView.prefHeightProperty().bind(container.heightProperty());
                currentContentView = listView;
            }
        });
        hBox.getChildren().addAll(tipLabel, spacer, treeButton, tableButton, docButton);

        container.getChildren().add(hBox);

        // 4、TableView
        tableView = new TableView();
        // 这句话让tableView占据container除了上面的部分的所有剩余的空间（container必须占据parent的空间，都是传递下来的， 缺一环不可）
        tableView.prefHeightProperty().bind(container.heightProperty());
        double time = System.currentTimeMillis();
        long records = loadFirstView();
        time = System.currentTimeMillis() - time;
        DecimalFormat formatter = new DecimalFormat("#.###### s", DecimalFormatSymbols.getInstance( Locale.ENGLISH ));
        formatter.setRoundingMode( RoundingMode.DOWN );
        String seconds = formatter.format(time/1000);
        String tip = "Find " + records + " documents." + "    Used: " + seconds;
        tipLabel.setText(tip);

        browser = loadWebview();
        container.getChildren().add(browser);
        currentContentView = tableView;

        tab1.setContent(container);

        return tab1;
    }

    public long loadFirstView() {
        long records = 0;

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        int count = 0;
        int limit = 50;
        int fieldIndex = 0;
        // 表头：因为zigzag数据可能存在的因素，搜索N个bson文件，用这些文件的key的最大集合做表头
        Map<Integer, String> fieldMap = new HashMap<>();
        while(cursor.hasNext() && count < limit) {
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            Iterator<String> iterator = keySet.iterator();
            while(iterator.hasNext()) {
                String field = iterator.next();
                if (!fieldMap.containsValue(field)) {
                    fieldMap.put(fieldIndex++, field);
                }
            }
            count++;
        }
        cursor.close();

        List<String> columns = new ArrayList<>(fieldMap.values());
        for (int ii = 0; ii < columns.size(); ii++) {
            final int finalIdx = ii;
            TableColumn<ObservableList<String>, String> column = new TableColumn<>(columns.get(ii));
            column.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().get(finalIdx)));
            column.setPrefWidth(150);
            tableView.getColumns().add(column);
        }

        // 表格内容
        cursor = findIterable.iterator();
        count = 0;
        ObservableList<ObservableList<String>> csvData = FXCollections.observableArrayList();
        while(cursor.hasNext() && records < limit) {
            Document document = cursor.next();
            ObservableList<String> row = FXCollections.observableArrayList();

            for (int index = 0; index < columns.size(); index++) {
                Object object = document.get(columns.get(index));
                if (object != null) {
                    //TODO: configurable (split line or not)
                    String content = object.toString();
                    String[] lines = content.split("\n");

                    row.add(lines[0]);
                    //TODO: use tooltip, and others
                    // https://stackoverflow.com/questions/12717969/javafx-2-custom-popup-pane
                }
                else {
                    row.add("");
                }
            }
            csvData.add(row); //每行的数据
            records++;
        }

        tableView.setItems(csvData); //所有的数据

        return records;
    }

    private void hideOtherView() {

    }

    private TreeTableView<DbObject> loadTableTreeView() {
        long records = 1;

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        List<DbObject> dbObjectList = new ArrayList<>();
        while(cursor.hasNext()) {
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();
                String type = object.getClass().toString();
                type = type.substring(type.lastIndexOf(".") + 1);
                DbObject o = new DbObject(key, value, type);
                objectList.add(o);
            }

            //每一个Document形成一个tree
            DbObject dbObject = new DbObject("(" + records + ") " + "ObjectId(\"" + document.get("_id").toString() + "\")", "{ " + keySet.size() + " fields }", "Object");
            dbObject.setObjectList(objectList);

            dbObjectList.add(dbObject);
            records++;
        }
        cursor.close();

        final TreeItem<DbObject> root = new TreeItem<>(new DbObject("", "", ""));
        for (int i = 0; i < dbObjectList.size(); ++i) {
            DbObject dbObject = dbObjectList.get(i);
            TreeItem<DbObject> parentItem = new TreeItem<>(dbObjectList.get(i));

            for (int index = 0; index < dbObject.getObjectList().size(); ++index) {
                TreeItem<DbObject> childItem = new TreeItem<>(dbObject.getObjectList().get(index));
                parentItem.getChildren().add(childItem);
            }

            root.getChildren().add(parentItem);
        }

        TreeTableColumn<DbObject, String> keyColumn = new TreeTableColumn<>("Key");
        keyColumn.setPrefWidth(350);
        keyColumn.setCellValueFactory(
                (TreeTableColumn.CellDataFeatures<DbObject, String> param) ->
                        new ReadOnlyStringWrapper(param.getValue().getValue().getKey())
        );

        TreeTableColumn<DbObject, String> valueColumn = new TreeTableColumn<>("Value");
        valueColumn.setPrefWidth(600);
        valueColumn.setCellValueFactory(
                (TreeTableColumn.CellDataFeatures<DbObject, String> param) ->
                        new ReadOnlyStringWrapper(param.getValue().getValue().getValue())
        );

        TreeTableColumn<DbObject, String> typeColumn = new TreeTableColumn<>("Type");
        typeColumn.setPrefWidth(100);
        typeColumn.setCellValueFactory(
                (TreeTableColumn.CellDataFeatures<DbObject, String> param) ->
                        new ReadOnlyStringWrapper(param.getValue().getValue().getType())
        );

        TreeTableView<DbObject> treeTableView = new TreeTableView<>(root);
        treeTableView.setShowRoot(false);

        treeTableView.getColumns().setAll(keyColumn, valueColumn, typeColumn);

        return treeTableView;
    }

    class DbObject {

        private SimpleStringProperty key;
        private SimpleStringProperty value;
        private SimpleStringProperty type;
        private List<DbObject> objectList;
        public SimpleStringProperty keyProperty() {
            if (key == null) {
                key = new SimpleStringProperty(this, "name");
            }
            return key;
        }
        public SimpleStringProperty valueProperty() {
            if (value == null) {
                value = new SimpleStringProperty(this, "email");
            }
            return value;
        }
        private DbObject(String name, String value, String type) {
            this.key = new SimpleStringProperty(name);
            this.value = new SimpleStringProperty(value);
            this.type = new SimpleStringProperty(type);
        }
        public String getKey() {
            return key.get();
        }
        public void setKey(String fName) {
            key.set(fName);
        }
        public String getValue() {
            return value.get();
        }
        public void setValue(String fName) {
            value.set(fName);
        }

        public String getType() {
            return type.get();
        }

        public SimpleStringProperty typeProperty() {
            return type;
        }

        public void setType(String type) {
            this.type.set(type);
        }

        public List<DbObject> getObjectList() {
            return objectList;
        }

        public void setObjectList(List<DbObject> objectList) {
            this.objectList = objectList;
        }
    }

    private ListView<Text> loadTextView() {
        long records = 1;

        ListView<Text> listView = new ListView<>();
        // 下面的background代码会导致看到黑线
//        Background background = new Background(new BackgroundFill(Color.rgb(0, 0, 0), null, null));
//        listView.setBackground(background);
//        listView.setStyle("-fx-control-inner-background: #494c4e;");
        listView.getStyleClass().add("mylist");

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        Text text;
        while(cursor.hasNext()) {
            // 1, 序号
            text = new Text("/*    " + records + "    */");
            text.setFill(Color.rgb(128, 141, 153));
            listView.getItems().add(text);

            // 2, head
            text = new TextExt("{") ;
            text.setFill(Color.rgb(227, 209, 77));
            listView.getItems().add(text);

            // 3, content
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();

                text = new Text("    \"" + key + "\"" + " : " + "    \"" + value + "\"");
                text.setFill(Color.rgb(198, 240, 121));
                listView.getItems().add(text);
            }

            // 4, tail
            text = new TextExt("}") ;
            text.setFill(Color.rgb(227, 209, 77));
            listView.getItems().add(text);

            // 2, 回车 + 空行
            listView.getItems().add(new Text(""));

            records++;
        }
        cursor.close();

        return listView;
    }

    private StyleClassedTextArea loadStyleClassedTextArea() {
        long records = 1;

        StyleClassedTextArea textArea = new StyleClassedTextArea();

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        String text = "";
        while(cursor.hasNext()) {
            // 1, 序号
            text += "/*    " + records + "    */\n";

            // 2, head
            text += "{\n";

            // 3, content
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();

                String line = "    \"" + key + "\"" + " : " + "    \"" + value + "\"\n";
                text += line;
            }

            // 4, tail
            text += "}\n";

            // 5, 回车 + 空行
            text += "\n";

            records++;
        }
        cursor.close();

        textArea.appendText(text);
        return textArea;
    }

    private TextFlow loadTextFlow() {
        TextFlow textFlow = new TextFlow();

        long records = 1;

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        String text = "";
        while(cursor.hasNext()) {
            // 1, 序号
            textFlow.getChildren().add(new Text("/*    " + records + "    */\n"));

            // 2, head
            textFlow.getChildren().add(new Text("{\n"));

            // 3, content
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();

                String line = "    \"" + key + "\"" + " : " + "    \"" + value + "\"\n";
                textFlow.getChildren().add(new Text(line));
            }

            // 4, tail
            textFlow.getChildren().add(new Text("}\n"));

            // 2, 回车 + 空行
            textFlow.getChildren().add(new Text("\n"));

            records++;
        }
        cursor.close();

        return textFlow;
    }

    private Label loadTextAreaView() {
        Label textArea = new Label();

        long records = 1;

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        String text = "";
        while(cursor.hasNext()) {
            // 1, 序号
            text += "/*    " + records + "    */\n";

            // 2, head
            text += "{\n";

            // 3, content
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();

                String line = "    \"" + key + "\"" + " : " + "    \"" + value + "\"\n";
                text += line;
            }

            // 4, tail
            text += "}\n";

            // 2, 回车 + 空行
            text += "\n";

            records++;
        }
        cursor.close();

        textArea.setText(text);

        return textArea;
    }

    private WebView loadWebview() {
        long records = 0;
        browser = new WebView();
        WebEngine webEngine = browser.getEngine();

        FindIterable<Document> findIterable = collection.find();
        MongoCursor<Document> cursor = findIterable.iterator();

        String text = "<html><br>";
        while(cursor.hasNext()) {
            // 1, 序号
            text += "<p>";
            text += "/*    " + records + "    */";
            text += "</p><br>";

            // 2, head
            text += "<p>";
            text += "{";
            text += "</p><br>";

            // 3, content
            Document document = cursor.next();
            String json = document.toJson();
            Gson gson = new Gson();
            JsonObject jo = gson.fromJson(json, JsonObject.class);
            Set<String> keySet = jo.keySet();
            List<DbObject> objectList = new ArrayList<>();
            for(String key: keySet) {
                Object object = document.get(key);
                String value = object.toString();

                String line = "    \"" + key + "\"" + " : " + "    \"" + value + "\"\n";
                text += "<p>";
                text += line;
                text += "</p><br>";
            }

            // 4, tail
            text += "<p>";
            text += "}";
            text += "</p><br>";

            // 5, 回车 + 空行
            text += "<br>";

            records++;
        }
        cursor.close();

        text += "</html>";
        webEngine.loadContent(text, "text/html");
        return browser;
    }
}


